﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Live;
using Windows.Storage;


/**
 * http://msdn.microsoft.com/en-us/library/live/hh826541.aspx#livesite_tailored_2
 * 
 * http://msdn.microsoft.com/zh-cn/library/live/hh826531#reading_folders
 * 
 * http://isdk.dev.live.com/dev/isdk/ISDK.aspx?category=scenarioGroup_core_concepts&index=0
 */
namespace NineDishes.Win8.DataModel
{
    public class SkyDriveLoader
    {
        private LiveConnectSession session;
        private LiveConnectClient liveClient;
        private SkyDriveProcessHandler processHandler;

        public SkyDriveLoader(SkyDriveProcessHandler _processHandler) 
        {
            this.processHandler = _processHandler;
        }

        public async Task<string> Login() 
        {
            if (this.session != null) 
            {
                return "Signed in."; ;
            }
            try
            {
                /**
                 * Message = "The access token doesn't have the correct permission for the resource. The access token must include one of the following scopes: 'wl.contacts_photos', 'wl.contacts_skydrive'."
                 */
                LiveAuthClient auth = new LiveAuthClient();
                LiveLoginResult loginResult = await auth.LoginAsync(new string[] { "wl.basic", "wl.contacts_skydrive", "wl.contacts_photos", "wl.skydrive_update" });
                if (loginResult.Status == LiveConnectSessionStatus.Connected)
                {
                    session = auth.Session;
                    liveClient = new LiveConnectClient(this.session);
                    return "Signed in.";

                }
                else
                {
                    return "Sign in Fail!";
                }

            }
            catch (LiveAuthException exception)
            {
                return "Error signing in: " + exception.Message;
            }
        }

        public async void ReadFolder()
        {
            if (this.session == null) {
                await this.Login();
            }
            try
            {
                LiveConnectClient liveClient = new LiveConnectClient(this.session);
                LiveOperationResult operationResult =
                    await liveClient.GetAsync("me/skydrive/files");
                dynamic result = operationResult.Result;

                List<object> data = (List<object>)result.data;
                foreach (IDictionary<string, object> content in data) {
                    string type = (string)content["type"];
                    if (type != "folder") {
                        // do something with folders?
                        continue;
                    }
                    string filename = (string)content["name"];
                    string fileId = (string)content["id"];

                    if (filename.ToLower().Equals("ninedishes"))
                    {
                        operationResult = await liveClient.GetAsync(fileId + "/files");
                        result = operationResult.Result;
                    }
                    // use fileId to download a file or list files in a folder

                    // there's a few more details available in content.Keys
                    // such as created_time and updated_time for those interested
                }
                //infoTextBlock.Text = "Folder name: " + result.name + ", ID: " + result.id;
            }
            catch (LiveConnectException exception)
            {
                //infoTextBlock.Text = "Error getting folder info: " + exception.Message;
            }
        }

        public async Task<IDictionary<string, object>> MakeDirIfNotExist(string name)
        {
            return await MakeDirIfNotExist(name, null);
        }

        public async Task<IDictionary<string, object>> MakeDirIfNotExist(string name, string forderId) 
        {
            var folderContent = await FindFile(name, forderId, "folder");

            if (folderContent != null) 
            {
                return folderContent;
            }

            var folderData = new Dictionary<string, object>();
            folderData.Add("name", name);
            //LiveConnectClient liveClient = new LiveConnectClient(this.session);
            LiveOperationResult operationResult =
                await liveClient.PostAsync(forderId == null ? "me/skydrive" : forderId, folderData);
            dynamic result = operationResult.Result;

            return result;
        }

        /// <summary>
        /// 
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="forderId"></param>
        /// <param name="filterType">folder</param>
        /// <returns>name/id</returns>
        public async Task<IDictionary<string, object>> FindFile(string name, string forderId, string filterType)
        {
            string root = forderId == null? "me/skydrive":forderId ;

            //LiveConnectClient liveClient = new LiveConnectClient(this.session);
            
            LiveOperationResult operationResult =
                await liveClient.GetAsync(root + "/files");
            dynamic result = operationResult.Result;

            List<object> data = (List<object>)result.data;
            foreach (IDictionary<string, object> content in data)
            {
                string type = (string)content["type"];
                if (filterType != null && !type.Equals(filterType))
                {
                    // do something with folders?
                    continue;
                }
                string filename = (string)content["name"];
                //string fileId = (string)content["id"];

                if (filename.ToLower().Equals(name.ToLower()))
                {
                    //operationResult = await liveClient.GetAsync(fileId + "/files");
                    //result = operationResult.Result;
                    return content;
                }
            }

            return null;
        }

        public async Task<IDictionary<string, object>> DownLoadAll()
        {
            if (!"Signed in.".Equals(await Login()))
            {
                throw new Exception("login fail");
            }

            IDictionary<string, object> content = await this.MakeDirIfNotExist("NineDishes") ;

            await DownLoadAll(null, (string)content["id"]);
            
            return null;
        }

        public async Task<IDictionary<string, object>> DownLoadAll(StorageFolder localFolder, string forderId)
        {            
            if (localFolder == null)
            {
                localFolder = ApplicationData.Current.LocalFolder;
                localFolder = await localFolder.CreateFolderAsync("Data", CreationCollisionOption.OpenIfExists);
            }
            //LiveConnectClient liveClient = new LiveConnectClient(this.session);

            LiveOperationResult operationResult =
                await liveClient.GetAsync(forderId + "/files");
            dynamic result = operationResult.Result;

            List<object> data = (List<object>)result.data;
            foreach (IDictionary<string, object> content in data)
            {
                string type = (string)content["type"];
                string filename = (string)content["name"];
                string fileId = (string)content["id"] ;

                if (type == "folder")
                {
                    StorageFolder subFolder = await localFolder.CreateFolderAsync(filename, CreationCollisionOption.OpenIfExists);
                    // do something with folders?
                    await DownLoadAll(subFolder, fileId);
                    continue;

                }

                StorageFile file = await localFolder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);

                if (processHandler == null)
                {
                    await liveClient.BackgroundDownloadAsync(fileId + "/content", file);
                }
                else
                {
                    processHandler.Content = filename;
                    await liveClient.BackgroundDownloadAsync(fileId + "/content", file, processHandler.CancellationTokenSource.Token, processHandler.Progress);
                }
            }

            return null;
        }


        public async Task<IDictionary<string, object>> UploadAll()
        {
            if (!"Signed in.".Equals(await Login()))
            {
                throw new Exception("login fail");
            }

            IDictionary<string, object> content = await this.MakeDirIfNotExist("NineDishes");

            await UploadAll(null, (string)content["id"]);

            return null;
        }

        public async Task<IDictionary<string, object>> UploadAll(StorageFolder localFolder, string forderId)
        {
            if (localFolder == null)
            {
                //localFolder = ApplicationData.Current.LocalFolder;
                localFolder = Windows.ApplicationModel.Package.Current.InstalledLocation;

                localFolder = await localFolder.CreateFolderAsync("Data", CreationCollisionOption.OpenIfExists);
            }

            IReadOnlyList<IStorageItem> items = await localFolder.GetItemsAsync();

            foreach (IStorageItem item in items)
            {
                StorageFolder subFolder = item as StorageFolder;

                if (subFolder == null)
                {
                    if (await this.FindFile(item.Name, forderId, null) == null)
                    {
                        if (processHandler == null)
                        {
                            await liveClient.BackgroundUploadAsync(forderId, item.Name, item as IStorageFile, OverwriteOption.DoNotOverwrite);
                        } 
                        else
                        {
                            processHandler.Content = item.Name;
                            await liveClient.BackgroundUploadAsync(forderId, item.Name, item as IStorageFile, OverwriteOption.DoNotOverwrite, processHandler.CancellationTokenSource.Token, processHandler.Progress);
                        }
                        
                    }
                }
                else
                {
                    IDictionary<string, object> content = await this.MakeDirIfNotExist(subFolder.Name, forderId);
                    await UploadAll(subFolder, (string)content["id"]);
                }
            }

            return null;
        }


        public async Task<string> ClearLocal()
        {
            StorageFolder localFolder = ApplicationData.Current.LocalFolder;
            try
            {
                localFolder = await localFolder.GetFolderAsync("Data");
            }
            catch (System.IO.FileNotFoundException) 
            {
                return "Not Files are deleted!";
            }
            
            await localFolder.DeleteAsync();

            return "OK";
        }
    }

    public class SkyDriveProcessHandler
    {
        public SkyDriveProcessHandler() 
        {
            this.CancellationTokenSource = new System.Threading.CancellationTokenSource(); 
        }

        public string Content { get; set; }
        public Progress<LiveOperationProgress> Progress { get; set; }
        public System.Threading.CancellationTokenSource CancellationTokenSource { get; set; }
    }
}
